#include "Game.h"

//#define DEBUG

#ifdef DEBUG
    #define DBG(x) std::cout << x << std::endl;
#else
    #define DBG(x)
#endif

CGame* CGame::s_pInstance = 0;

void CGame::init(int iWidth, int iHeight, int iBits, bool bFullscreen)
{
    
    m_pRenderer         = new CRenderer();
    m_pTextureHandler   = new CTextureHandler();
    m_pInput            = new CInput();
    m_pTimer            = new CTimer();
    m_pCamera           = new C2DCamera();
    m_pWorld            = new CWorld();
    
    m_pRenderer->CreateRendererWindow("2D Pixel Game", iWidth, iHeight, iBits, bFullscreen);
    
}

void CGame::start(String sModName, String sLevelName, int iPlayerCount)
{
    
    DBG("Initialising game");
    
    m_sCurModName = sModName;
    
    m_pWorld->loadLevel(sLevelName);
    DBG("Level loaded");
    /*
    CEntity* player1 = 0;
    CEntity* player2 = 0;
    
    //TODO: Remove this hack
    if (sLevelName == "level4") {
        CEntity player("Ben", Vector(-200, 300), "Wheel", RGBAColor(0, 1, 1, 1));
        CEntity player_2("Ben", Vector(-250, 300), "Wheel", RGBAColor(1, 1, 1, 1));
        
        player1 = m_World.addEntity(player);
        player2 = m_World.addEntity(player_2);
    }
    else {
        CEntity player("Ben", Vector(0, 0), "Wheel", RGBAColor(0, 1, 1, 1));
        CEntity player_2("Ben", Vector(50, 0), "Wheel", RGBAColor(1, 1, 1, 1));
        
        player1 = m_World.addEntity(player);
        player2 = m_World.addEntity(player_2);
    }
    //TODO: Decide whether it's better to use pointers
    
    */
    //for (int i=0;i<1;++i)
    //int i=0;
        //m_World.addPlayer( CPlayer("Player", Vector(-150.f + i * 1.f, -50.f), "Bunny1", RGBAColor(1, 0, 1, 1)) );
    
    //player1->applyForce(Vector(0.8f, 0));
    
    CEntity* pWheel1 = m_pWorld->addEntity("Wheel1", Vector(0, 0), "Wheel", RGBAColor(1, 1, 1, 1));
    CEntity* pWheel2 = m_pWorld->addEntity("Wheel2", Vector(50, 0), "Wheel", RGBAColor(1, 1, 1, 1));
    CEntity* pChassis = m_pWorld->addEntity("Chassis", Vector(25, 25), "Chassis", RGBAColor(1, 1, 1, 1));
    
    CLengthConstraint* pWheelBase = m_pWorld->addLengthConstraint( CPhysicsSegment(pWheel1->getPhysics(), pWheel2->getPhysics()) );
    m_pWorld->addSpringConstraint( CPhysicsSegment(pChassis->getPhysics(), pWheel1->getPhysics()), 0.05f, 0.2f );
    m_pWorld->addSpringConstraint( CPhysicsSegment(pChassis->getPhysics(), pWheel2->getPhysics()), 0.05f, 0.2f );
    
    //TODO: Work out what to do with CPoint construction
    
    CPhysicsPoint* pRailStart = m_pWorld->addPhysicsPoint(Vector(0, 0));
    CPhysicsPoint* pRailEnd = m_pWorld->addPhysicsPoint(Vector(200, 10));
    //m_pWorld->addSlideConstraint(pWheel2->getPhysics(), CSegment(pRailStart, pRailEnd));
    
    pChassis->alignWith(CSegment(pWheel1->getPhysics(), pWheel2->getPhysics()));
    
    DBG("Entities added");
    
	//m_Camera.setZoom(0.002f);
    
    bool bRunning = true;
    
    DBG("Beginning main loop");
    
    while (bRunning) {
        
        //Check for windows messages
		if (m_pRenderer->CheckMessages()) continue;
		
		//Make sure the renderer is currently active
		if (!m_pRenderer->isActive()) continue;
        
		//Check if the Engine has recieved a quit message
		if (m_pRenderer->isQuit()) bRunning = false;
        
		//Error checking
		int r = glGetError();
		if (r != GL_NO_ERROR) { std::cout << "OpenGL Error " << r << std::endl; }
        
        //Gather input
        m_pInput->CheckInput();
        
        //Check for quit 
        if (m_pInput->KeyState(VK_ESCAPE) == true) bRunning = false;
        
        if (m_pInput->KeyState(VK_SPACE))
            std::cout << m_pTimer->getFPS() << std::endl;
        
        //Check for camera controls
        if (m_pInput->KeyState(VK_LBUTTON) && m_pInput->KeyState(VK_RBUTTON)) {
            m_pCamera->setZoom(m_pCamera->getZoom() - 0.02f * m_pInput->MouseMoveVector()[1]);
        }
        else if (m_pInput->KeyState(VK_LBUTTON)) {
			m_pCamera->setAngle(m_pCamera->getAngle() - m_pInput->MouseMoveVector()[0]);
		}
        else if (m_pInput->KeyState(VK_RBUTTON)) {
            m_pCamera->setPos(m_pCamera->getPos() + 1.f * Vector(m_pInput->MouseMoveVector()[0], -m_pInput->MouseMoveVector()[1]));
		}
        
        /*if (m_Input.KeyState(VK_SPACE))
            m_fGameSpeed = 0.1;
        else
            m_fGameSpeed = 1.0;*/
        
        //player1->getAngle() -= m_Input.MouseMoveVector()[0] / 800 * 2*PI;
        
        //pChassis->getAngle() = -(pWheel1->getPos() - pWheel2->getPos()).getAngle();
        
        Scalar fAccelForce = 2.0f;
        
        if (m_pInput->KeyState('B'))
            pWheel1->getPhysics()->getAngVel() = 0;
        
        if (m_pInput->KeyState(VK_UP)) {
            pWheel1->applyForce(Vector(-fAccelForce, 0), Vector(0, -1));
            pWheel1->applyForce(Vector(fAccelForce, 0), Vector(0, 1));
        }
        if (m_pInput->KeyState(VK_DOWN)) {
            pWheel1->applyForce(Vector(fAccelForce, 0), Vector(0, -1));
            pWheel1->applyForce(Vector(-fAccelForce, 0), Vector(0, 1));
        }
        if (m_pInput->KeyDown(VK_LEFT)) {
            
            Vector vNormal = CPhysicsSegment(pWheel1->getPhysics(), pWheel2->getPhysics()).getVector().getUnitVector().getNormal();
            
            pWheel1->applyForce(1.0f * vNormal);
            pWheel2->applyForce(1.0f * -vNormal);
            
        }
            //pWheel1->getPhysics().getVel()[0] = -5.f;
        if (m_pInput->KeyDown(VK_RIGHT)) {
            
            Vector vNormal = CPhysicsSegment(pWheel1->getPhysics(), pWheel2->getPhysics()).getVector().getUnitVector().getNormal();
            
            pWheel1->applyForce(1.0f * -vNormal);
            pWheel2->applyForce(1.0f * vNormal);
            
        }
            //pWheel1->getPhysics().getVel()[0] = 5.f;
        
        
        
        //Track the player with the camera (center on player always)
        m_pCamera->setPos(-pWheel1->getPos());
        
        //player1->applyForce(-vBetween / fLength * (fLength - fDist) * 0.01f);
        //player2->applyForce(vBetween / fLength * (fLength - fDist) * 0.01f);
        //player1->applyForce(-vBetween * 0.5 * (vBetween % player1->getPhysics().getVel()));
        //player2->applyForce(vBetween * 0.5 * (vBetween % player2->getPhysics().getVel()));
        
        //Apply camera transformations
        //glTranslatef(vCameraTrans[0], -vCameraTrans[1], 0.f);
        
		//glRotatef(vCameraRot[0], 0, 1, 0);
    	//glRotatef(vCameraRot[1], 1, 0, 0);
        
        m_pCamera->update();
        
        m_pTimer->update();
        
        //Draw axes
        Scalar fScaleLength = 10.f;
        glPushMatrix();
        //glTranslatef(-2.f*fScaleLength, -2.f*fScaleLength, 0.f);
        glBegin(GL_LINES);
    		glColor4f(1, 0, 0, 1);
            glVertex3f(0, 0, 0);
            glVertex3f(fScaleLength, 0, 0);
            glColor4f(0, 1, 0, 1);
            glVertex3f(0, 0, 0);
            glVertex3f(0, fScaleLength, 0);
            glColor4f(0, 0, 1, 1);
            glVertex3f(0, 0, 0);
            glVertex3f(0, 0, fScaleLength);
        glEnd();
        glPopMatrix();
        
        //Update the game world
        m_pWorld->update(m_fGameSpeed);
        DBG("Updated successfully");
        //Draw the game world
        m_pWorld->draw();
        DBG("Drew successfully");
        //Render the whole scene
		m_pRenderer->DrawScene(); 
        
    }
    
}

void CGame::cleanup()
{
    
    //Cleanup
    delete m_pWorld;
    m_pWorld = 0;
    delete m_pCamera;
    m_pCamera = 0;
    delete m_pTimer;
    m_pTimer = 0;
    delete m_pInput;
    m_pInput = 0;
    delete m_pTextureHandler;
    m_pTextureHandler = 0;
    delete m_pRenderer;
    m_pRenderer = 0;
    
}
